import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import lib.Board;
import lib.Cell;

public class OthelloBoard extends Board<OthelloCellState, OthelloMove> {

	Integer[][] directionMatrix = new Integer[8][2];
	Player currentPlayer ;
	Player waitPlayer ;

	public OthelloBoard () {
		super (8, 8) ;
		
		directionMatrix[0][0] = -1; directionMatrix[0][1] = 0;
		directionMatrix[1][0] = -1; directionMatrix[1][1] = 1;
		directionMatrix[2][0] = 0; directionMatrix[2][1] = 1;
		directionMatrix[3][0] = 1; directionMatrix[3][1] = 1;
		directionMatrix[4][0] = 1; directionMatrix[4][1] = 0;
		directionMatrix[5][0] = 1; directionMatrix[5][1] = -1;
		directionMatrix[6][0] = 0; directionMatrix[6][1] = -1;
		directionMatrix[7][0] = -1; directionMatrix[7][1] = -1;
		
		boardState[3][3] = new OthelloCellState (Player.WHITE) ;
		boardState[4][4] = new OthelloCellState (Player.WHITE) ;
		boardState[3][4] = new OthelloCellState (Player.BLACK) ;
		boardState[4][3] = new OthelloCellState (Player.BLACK) ;
		
		currentPlayer = Player.BLACK ;
		waitPlayer = Player.WHITE ;
	}
	
	public void actionOnCell(Cell cell) {
		revertCells(cell);
		calculatePoints();
		switchPlayers();
		if (isGameOver()) {
			Othello.gameOver(getWinner());
			System.out.println ("Gameover");
		} else if (getMoves().isEmpty()) {
			switchPlayers();
		} else {
			botPlay();
		}
	}
	
	private void botPlay() {
		Cell cell = OthelloMinimax.getBestMove(this, currentPlayer);
		revertCells(cell);
		calculatePoints();
		switchPlayers();
		if (isGameOver()) {
			Othello.gameOver(getWinner());
			System.out.println ("Gameover");
		} else if (getMoves().isEmpty()) {
			switchPlayers();
			botPlay();
		}
	}

	private void revertCells(Cell baseCell) {
		turnCells(baseCell);
		setCellState(baseCell, new OthelloCellState (currentPlayer));
	}

	private void calculatePoints() {
		Othello.setBlackPoints(getPoints(Player.BLACK));
		Othello.setWhitePoints(getPoints(Player.WHITE));
	}

	private void switchPlayers() {
		Player aux = currentPlayer;
		currentPlayer = waitPlayer;
		waitPlayer = aux;
		Othello.setTurnPlayer();
	}
	
	private void turnCells(Cell baseCell) {
		List<Cell> oppositeCellsInDirection;
		int rowAdd;
		int colAdd;
		
		for (int i = 0; i < 8; i++) {
			rowAdd = directionMatrix[i][0];
			colAdd = directionMatrix[i][1];
			oppositeCellsInDirection = getOppositeCellsInDirection(baseCell, rowAdd, colAdd);
			if (oppositeCellsInDirection != null) {
				for (Cell cell : oppositeCellsInDirection) {
					getCellState(cell).setPlayer(currentPlayer);
				}
			}
		}
	}
 	
	private List<Cell> getOppositeCellsInDirection(Cell baseCell, int rowAdd, int colAdd) {	
		List<Cell> oppositeCells;
		Cell cell = new Cell(baseCell.getRow(), baseCell.getCol());
		cell.setRow(cell.getRow() + rowAdd);
		cell.setCol(cell.getCol() + colAdd);
		
		if (!isValidCellBoard(cell) ||
			getCellState(cell) == null ||
			getCellState(cell).getPlayer().equals(currentPlayer)) return null;
		
		oppositeCells = new ArrayList<Cell>();
		while (!getCellState(cell).getPlayer().equals(currentPlayer)) {
			oppositeCells.add(new Cell(cell.getRow(), cell.getCol()));
			
			cell.setRow(cell.getRow() + rowAdd);
			cell.setCol(cell.getCol() + colAdd);
			
			if (!isValidCellBoard(cell) || getCellState(cell) == null) return null;
		}
		
		return oppositeCells;
	}
	
	public Player getWinner(){
		int black = getPoints(Player.BLACK);
		int white = getPoints(Player.WHITE);
		if ( black < white) return Player.WHITE;
		if (black > white) return Player.BLACK;
		return null;
	}
	
	public int getPoints(Player player) {
		int cont = 0;
		for (int row = 0; row < getRowCount(); row++){
			for (int col=0; col < getColCount(); col++){
				if (getCellState(row, col) != null &&
						getCellState(row, col).getPlayer().equals(player)) {
					cont++;
				}
			}
		}
		return cont;
	}
	
	public int getDeltaPoints(Player player) {
		int delta = getPoints(Player.BLACK) - getPoints(Player.WHITE);
		return player.equals(Player.BLACK) ? delta : -delta ;
	}
	
	public int getCurrentPlayerPoints() {
		return getPoints(currentPlayer);
	}
	
	public List<Cell> getMoves() {
		Set<Cell> allowedCellSet = new HashSet<Cell>();
		for (int row = 0; row < getRowCount(); row++) {
			for (int col = 0; col < getColCount(); col++) {

				if (getCellState(row, col) != null) {
					continue;
				}
				
				for (int i = 0; i < 8; i++) {
					final Integer rowAdd = directionMatrix[i][0];
					final Integer colAdd = directionMatrix[i][1];
					if (getOppositeCellsInDirection(new Cell(row, col), rowAdd, colAdd) != null) {
						allowedCellSet.add(new Cell(row, col));
						break;
					}
				}
				
			}
		}
		return new ArrayList<Cell>(allowedCellSet);
	}
	
	public OthelloBoard makeMoveAndClone(Cell cell) {
		OthelloBoard board = clone();
		board.revertCells(cell);
		board.switchPlayers();
		return board;
	}
	
	public boolean isGameOver() {
		int cont = 0;
		for (int row = 0; row < getRowCount(); row++){
			for (int col=0; col < getColCount(); col++){
				if (getCellState(row, col) != null) {
					cont++;
				}
			}
		}
		return cont == getColCount()*getRowCount();
	}
	
	public OthelloBoard clone() {
		OthelloBoard board = new OthelloBoard();
		board.currentPlayer = currentPlayer;
		board.waitPlayer = waitPlayer;
		for (int row = 0; row < getRowCount(); row++) {
			for (int col = 0; col < getColCount(); col++) {
				if (getCellState(row, col) != null) {
					Player player = getCellState(row, col).getPlayer();
					board.setCellState(row, col, new OthelloCellState(player));
				}
			}
		}
		return board;
	}
	
	public Player getCurrentPlayer() {
		return currentPlayer;
	}
}
